<?php

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;

class DefaultDb_Repositories_ScheduleRepository extends EntityRepository
{

    public function getNextSchedulesByRouteAndPointBranch($userId, $pointIdOrigin, $limit = 3, $recurrent = true)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $stringRecurrent = $recurrent === false ? '' : ' OR s.recurrent = 1 ';
        $query = '
                SELECT s.*
                FROM schedule s
                INNER JOIN route_points rp ON rp.route_id = s.route_id
                #INNER JOIN users u ON u.point_id = rp.point_id
                INNER JOIN branches_user u ON u.point_id = rp.point_id 
                WHERE 
                ( 
                TIMESTAMP(s.start_date) > DATE(NOW()) 
                ' . $stringRecurrent . '
                )
                    AND u.client_id = ' . $userId . '
                    AND rp.point_id = ' . $pointIdOrigin . '
                    AND rp.status = ' . DefaultDb_Entities_RoutePoint::STATUS_NORMAL . '
                    AND s.status = ' . DefaultDb_Entities_Schedule::STATUS_ACTIVE . '
                ORDER BY s.start_date 
                LIMIT ' . $limit . '
            ';
        //      echo $query;
        $res = $conn->executeQuery($query);
        $array = $res->fetchAll();
        return $array;
    }

    /**
     * 
     * @param type $userId
     * @param type $pointIdOrigin
     * @param DateTime $date
     * @param type $limit
     * @param type $recurrent
     * @return type
     */
    public function getNextSchedulesByRouteAndPointBranchAndDate($userId, $pointIdOrigin, $date, $limit = 3, $recurrent = true)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $stringRecurrent = $recurrent === false ? '' : ' OR s.recurrent = 1 ';
        $query = '
                SELECT s.*
                FROM schedule s
                INNER JOIN route_points rp ON rp.route_id = s.route_id
                INNER JOIN branches_user u ON u.point_id = rp.point_id 
                WHERE 
                ( 
                DATE(s.start_date) >= DATE(\'' . $date->format('Y-m-d') . '\') 
                ' . $stringRecurrent . '
                )
                    AND u.client_id = ' . $userId . '
                    AND rp.point_id = ' . $pointIdOrigin . '
                    AND rp.status = ' . DefaultDb_Entities_RoutePoint::STATUS_NORMAL . '
                ORDER BY s.start_date 
                LIMIT ' . $limit . '
            ';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    public function getNextSchedulesByPointId($pointId)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
        SELECT s.*
	FROM schedule s
	INNER JOIN route_points rp ON rp.route_id = s.route_id
	WHERE date(s.start_date) > date(NOW())
		AND rp.point_id = ' . $pointId . '
	ORDER BY s.start_date';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    public function getNextSchedulesByRouteByDate($route, $date)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
        SELECT s.*
	FROM schedule s
	WHERE 
        ( timestamp(s.start_date) >= timestamp(\'' . $date->format('Y-m-d H:i:s') . '\')
                OR s.recurrent = 1 )
		AND s.route_id = ' . $route->getId() . '
                AND s.status = ' . DefaultDb_Entities_Schedule::STATUS_ACTIVE . '
	ORDER BY s.start_date';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    /**
     * Se calcula el tiempo a parti del siguiente algoritomo, SE REGRESA EN MILISEGUNDOS.
     * 1. se obtienen todos los puntos de mi ruta, ordenados hasta mi punto de distribucion ($myPointOrder)
     * 2. de ests puntos se suma el tiempo (NO se toma en cuenta el punto 1, porque el tiempo indica del ultimo punto hacia el )
     * 3. se cuentan cuantos puntos son hacia mi punto y se multiplica por la constante de tiempo de configuracion.
     *
     * @param integer $routeId
     * @param integer $myPointOrder
     * @param integer $orderInicial Se Utiliza para sabaer en que orden esta el punto inicial ya que no siempre es el 1
     * @return integer
     */
    public function calculeShippidTime($routeId, $myPointOrder, $orderInicial = 1)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();

        $query = '
                SELECT # DefaultDb_Repositories_ScheduleRepository.calculeShippidTime
                    # Calculamos el tiempo que se tarde en llegar un vehiculo que se
                    # encuentra recorriendo una ruta.
                    # Sumamos los tiempos que se tarda en llegar al punto que queremos
                    # visitar, descartamos el primer punto ya que de el partimos con los
                    # paquetes cargados, ademas consideramos que en cada punto se tarda
                    # una cantidad X (configuration.minutes_per_point) en cargar / descargar
                    # los paquetes. 
                    IFNULL((COUNT(rp.id) - 1)  /* Descontamos uno ya que para el ultimo punto ya nos encontramos en el lugar que queriamos visitar*/ * rp.minutes_per_point * 60 + SUM(TIME_TO_SEC(rp.arrival_time)), 0) timeInSeconds
                FROM
                (
                    SELECT rp.id, c.minutes_per_point, rp.arrival_time
                    FROM route_points rp
                    # Hacemos un inner con la configuración para obtener el tiempo
                    # que se tarda en cargar / descargar los paquetes
                    INNER JOIN configurations c ON c.id = 1 
                    WHERE 
                            route_id = ' . $routeId . '
                        AND order_number <= ' . $myPointOrder . ' # Buscamos todos los puntos que esten antes del punto al que voy
                        AND rp.status = ' . DefaultDb_Entities_RoutePoint::STATUS_NORMAL . ' # puntos activos
                    ORDER BY order_number
                    LIMIT 1, 1000000 # Ignoramos el primer punto de venta y especificamos un "Limite" de puntos de venta en una ruta
                ) rp
            ';
        $res = $conn->executeQuery($query);
        $res = $res->fetch();
        return $res;
    }

    public function deleteSheduleDate($scheduled)
    {
        $em = $this->getEntityManager();
        $em->remove($scheduled);
        $em->flush();
    }

    /**
     * Regresa el centro de intercambio donde se intersectan dos rutas, y el orden maximo de esa ruta
     * para posteriormente calcular el tiempo que tarda en dar la vuelta completa una ruta
     * @param type $route
     * @param type $secondRoute
     * @return type
     */
    public function getIntersectionAndMaxRoutePointExchangeCenter($route, $secondRoute)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
         #SELECCIONA LA INTERSECCION DE DOS RUTAS Y REGRESA EL PUNTO MAXIMO DE LA PRIMERA RUTA
        SELECT A.idpoint,A.name,A.type,A.idroutepoint,A.route_id,max(A.idroutepoint) AS max_id_routepoint from 
        (
                SELECT * FROM (
                        SELECT id AS idpoint,name,type from points WHERE type = ' . DefaultDb_Entities_Point::TYPE_EXCHANGE_CENTER . '
                        AND status = ' . DefaultDb_Entities_Point::STATUS_NORMAL . '
                        ) pa 
                LEFT JOIN (
                        SELECT id AS idroutepoint,route_id,point_id,order_number FROM route_points
                        WHERE route_id =' . $route->getId() . '
                        AND status = ' . DefaultDb_Entities_RoutePoint::STATUS_NORMAL . '    
                        ) rta
                ON pa.idpoint = point_id WHERE rta.route_id =' . $route->getId() . '
        ) A
        INNER JOIN 
        (
                SELECT * FROM (
                        SELECT id AS idpoint,name,type from points WHERE type = ' . DefaultDb_Entities_Point::TYPE_EXCHANGE_CENTER . '
                        AND status = ' . DefaultDb_Entities_Point::STATUS_NORMAL . '
                        ) pa 
                LEFT JOIN (
                        SELECT id as idroutepoint,route_id,point_id,order_number FROM route_points
                        WHERE route_id =' . $secondRoute->getId() . '
                        AND status = ' . DefaultDb_Entities_RoutePoint::STATUS_NORMAL . '   
                        ) rta
                ON pa.idpoint = point_id WHERE rta.route_id =' . $secondRoute->getId() . '
        )
        B
        ON A.idpoint = B.idpoint LIMIT 0,1';
        $res = $conn->executeQuery($query);
        $res = $res->fetch();
        return $res;
    }

    /**
     *  No esta siendo utilizado
     * @param type $route
     * @return type
     */
    public function getNextRoute($route)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
        SELECT DISTINCT route_id 
        FROM route_points 
        WHERE point_id = (SELECT DISTINCT idpoint FROM (
                SELECT id AS idpoint,name,type from points WHERE type = ' . DefaultDb_Entities_Point::TYPE_EXCHANGE_CENTER . '
                ) pa 
            LEFT JOIN (
                SELECT id AS idroutepoint,route_id,point_id,order_number FROM route_points
                WHERE route_id =' . $route->getId() . '
                ) rta
            ON pa.idpoint = point_id WHERE rta.route_id =' . $route->getId() . ')
        and route_id <> ' . $route->getId();

        $res = $conn->executeQuery($query);
        $res = $res->fetch();
        return $res;
    }

    /**
     * Regresa un schedule por usuario y fecha del schedule
     * @param DefaultDb_Entities_User $user Usuario a buscar
     * @param dateTime $date Fecha a buscar 
     * @return mixed Regresa un schedule o null
     */
    public function getScheduleByUserAndDate($user, $date)
    {
        $em = $this->getEntityManager();
        $dql = '
        SELECT s
        FROM DefaultDb_Entities_Schedule s 
        WHERE s.user = ' . $user->getId() . '
        AND( s.startDate BETWEEN \'' . $date->format('Y-m-d 00:00:00') . '\' AND\'' . $date->format('Y-m-d 23:59:59') . '\' )
        AND s.status = ' . DefaultDb_Entities_Schedule::STATUS_ACTIVE . '
	ORDER BY s.startDate ';
        $query = $em->createQuery($dql)->setMaxResults(1);
        $res = $query->getOneOrNullResult();
        return $res;
    }

    /**
     * Buscamos las rutas agendadas que tiene un conductor.
     * @param int $user
     * @return array
     */
    public function getSchedulesByUserAndDate($userId)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
            # Buscamos las rutas agendadas que tiene un conductor.
            SELECT
                s.id schedule_id, # Necesario para poder buscar los puntos de esta ruta agendada,
                r.name,
                r.code,
                # Calculamos la fecha en que va a iniciar la ruta. Esto es necesario ya que NO podemos
                # tomar directa la fecha de schedule para el caso de las rutas recurrentes.
                # Para esto formamos la fecha de inicio de la ruta bajo estos 2 criterios:
                # 1) Tomamos la parte de la fecha (Y-m-d) de la fecha en que la ruta debio de pasar por
                #    el paquete;
                # 2) Tomamos la parte de la hora (H:i:s) de la fecha del inicio de la ruta agendada segun schedule.
                # IMPORTANTE!!!
                # Tomar en cuenta que si se cambia la hora de inicio de la ruta agendada los registros
                # anteriores se veran afectados, ya que se toma el valor directo de schedule.
                str_to_date(CONCAT(DATE_FORMAT(sa.route_date, "%Y-%m-%d"), " ", DATE_FORMAT(s.start_date, "%H:%i:%s")), "%Y-%m-%d %H:%i:%s") startDateTimeRoute,
                v.name vehicleName,
                v.capacity vehicleCapacity
            FROM sequential_activities sa
            INNER JOIN m3_commerce_order m3co ON m3co.id = sa.order_id
            # NO tomar en cuenta el estatus para permitir que las rutas agendadas se puedan recorrer.
            INNER JOIN schedule s ON s.id = m3co.schedule_id 
            # NO tomar en cuenta el estatus para permitir que las rutas agendadas se puedan recorrer.
            INNER JOIN routes r ON s.route_id = r.id 
            # Por el momento no tomamos en cuenta el estatus del vehiculo
            INNER JOIN vehicles v ON s.vehicle_id = v.id
            WHERE 
                    DATE(sa.route_date) >= DATE(now()) # que los tenga que visitar desde hoy hasta el infinito y mas alla
                AND sa.shipping_date IS NULL # todos los puntos que no han sido visitados
                AND s.user_id = ' . $userId . ' # Que pertenezcan a este conductor
            GROUP BY startDateTimeRoute # Agrupamos para solo traernos una vez cada ruta agendada.
            ORDER BY startDateTimeRoute ASC';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    public function getScheduleById($id)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
            # Buscamos las rutas agendadas que tiene un conductor.
            SELECT
                s.id schedule_id, # Necesario para poder buscar los puntos de esta ruta agendada,
                r.name,
                r.code,
                r.id route_id,
                # Calculamos la fecha en que va a iniciar la ruta. Esto es necesario ya que NO podemos
                # tomar directa la fecha de schedule para el caso de las rutas recurrentes.
                # Para esto formamos la fecha de inicio de la ruta bajo estos 2 criterios:
                # 1) Tomamos la parte de la fecha (Y-m-d) de la fecha en que la ruta debio de pasar por
                #    el paquete;
                # 2) Tomamos la parte de la hora (H:i:s) de la fecha del inicio de la ruta agendada segun schedule.
                # IMPORTANTE!!!
                # Tomar en cuenta que si se cambia la hora de inicio de la ruta agendada los registros
                # anteriores se veran afectados, ya que se toma el valor directo de schedule.
                str_to_date(CONCAT(DATE_FORMAT(sa.route_date, "%Y-%m-%d"), " ", DATE_FORMAT(s.start_date, "%H:%i:%s")), "%Y-%m-%d %H:%i:%s") startDateTimeRoute,
                v.name vehicleName,
                v.capacity vehicleCapacity
            FROM sequential_activities sa
            INNER JOIN m3_commerce_order m3co ON m3co.id = sa.order_id
            # NO tomar en cuenta el estatus para permitir que las rutas agendadas se puedan recorrer.
            INNER JOIN schedule s ON s.id = m3co.schedule_id 
            # NO tomar en cuenta el estatus para permitir que las rutas agendadas se puedan recorrer.
            INNER JOIN routes r ON s.route_id = r.id 
            # Por el momento no tomamos en cuenta el estatus del vehiculo
            INNER JOIN vehicles v ON s.vehicle_id = v.id
            WHERE s.id = ' . $id . '
            GROUP BY startDateTimeRoute # Agrupamos para solo traernos una vez cada ruta agendada.
            ORDER BY startDateTimeRoute ASC';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    /**
     * Buscamos los puntos que se visitaran durante una ruta agendada
     * @param integer $scheduleId
     * @return array
     */
    public function getPointsById($scheduleId)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        $query = '
            SELECT 
                p.code pointCode
              , p.name pointName
              , p.address pointAddress
              , sa.shipping_date
              , sa.route_date
          FROM schedule s
          INNER JOIN m3_commerce_order m3co ON m3co.schedule_id = s.id
          INNER JOIN sequential_activities sa ON m3co.id = sa.order_id
          INNER JOIN route_points rp ON sa.routePoint_id = rp.id
          INNER JOIN points p ON rp.point_id = p.id
          WHERE s.id = ' . $scheduleId . '
          ORDER BY rp.order_number ASC';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;
    }

    /**
     * 
     * @param integer $routeId Id de la ruta que queremos buscar su agenda
     */
    public function getByRouteId($routeId)
    {
        $em = $this->getEntityManager();
        /*$conn = $em->getConnection();
        $query = '
            SELECT * 
            FROM schedule
            WHERE 
                route_id = ' . $routeId . '
                AND status = ' . DefaultDb_Entities_Schedule::STATUS_ACTIVE . '
            ORDER BY start_date ASC';
        $res = $conn->executeQuery($query);
        $res = $res->fetchAll();
        return $res;*/
        $dql = 'SELECT s 
                FROM DefaultDb_Entities_Schedule s
                WHERE
                    s.route = '.  $routeId . '  
                AND s.status = ' . DefaultDb_Entities_Schedule::STATUS_ACTIVE . ' 
                ORDER BY s.startDate ASC';
        $query = $em->createQuery($dql);
        $schedules = $query->getResult();
        return $schedules;
    }

    /**
     * 
     * @param type $user
     * @param type $date
     * @return type
     */
    public function getByUserAndDate($user, $date)
    {
        $em = $this->getEntityManager();
        $dql = '
            SELECT rp 
            FROM DefaultDb_Entities_RoutePoint rp 
            JOIN rp.point p
            JOIN rp.route r
            WHERE r.id = ' . $routeId . '
            AND p.type = ' . $pointType . '
            ORDER BY p.name ASC';
        $query = $em->createQuery($dql);
        $routePoints = $query->getResult();
        return $routePoints;
    }

}